package com.skyon.rule.res.spring;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

import org.drools.runtime.StatefulKnowledgeSession;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.skyon.rule.res.exceptions.DroolsFireException;
import com.skyon.rule.res.exceptions.DroolsInsertException;
import com.skyon.rule.res.exceptions.GetFiredObjectException;
import com.skyon.rule.res.exceptions.PlotBuilderException;
import com.skyon.rule.res.exceptions.PlotNotFoundException;
import com.skyon.rule.res.model.ErrorMsg;
import com.skyon.rule.res.model.InputParam;
import com.skyon.rule.res.model.InputUrlAdapter;
import com.skyon.rule.res.model.OutputParam;
import com.skyon.rule.res.utils.ReflectGetValue;

/**
 * 辅助类，用于对SessionFactory操作的封装
 * 
 * @author cfans
 *
 */


@Component("plotServerHelper")
public class PlotServerHelper {
	
	private static Logger logger = LoggerFactory.getLogger( PlotServerHelper.class );
	private static final String EMPTY = "";
	@Autowired 
	private LocalSessionFactory localSessionFactory;


	public ErrorMsg valid(InputParam ipm) throws PlotBuilderException {
		ErrorMsg errMsg = new ErrorMsg();
		if (this.localSessionFactory == null) {
			errMsg.setErrorCode(ErrorMsg.DROOLS_ERROR);
			throw new PlotBuilderException("Could not find local factory bean in context",new Exception());
		}
		return errMsg; //this.localFactory.valid(ipm);
	}

	/**
	 * ruleURL 如 com.ccb.tfs.n2fs/sample?return=OutBean1,OutBean2
	 *               访问sample规则流
	 *               默认返回对象 OutBean1.propName;...;OutBean2.propName 形式
	 *           com.ccb.tfs.n2fs/?return=OutBean
	 *               访问默认主规则流
	 *               默认返回对象 OutBean.propName 形式
	 *           com.ccb.tfs.n2fs/sample?
	 *               访问sample规则流
	 *               默认返回所有对象 OutBeanx.propName 形式
	 *           com.ccb.tfs.n2fs/
	 *               访问默认主规则流
	 *               默认返回所有对象 OutBeanx.propName 形式
	 * 其中/ 有且只有一个，是判断是否合法的标准
	 * 要求
	 *   使用部署的主文件名（不要扩展名）作为规则访问的域名
	 *   调用者的规则流文件名和Process ID一致
	 *   
	 * @param inpp
	 * @return
	 * @throws Exception
	 */
	public OutputParam execute(InputParam inpp) throws Exception {
		if (this.localSessionFactory == null) {
			throw new PlotBuilderException("Could not find local factory bean in context ",new Exception());
		}
		
		OutputParam oppm = new OutputParam();
		InputUrlAdapter inputUrlAdapter = inpp.getInputUrlAdapter();
		oppm.setPlotId(inputUrlAdapter.toString());
		String root = inputUrlAdapter.getRoot();
		String processID = inputUrlAdapter.getProcessID();
		String[] outClsArr = inputUrlAdapter.getRetObjs();
		logger.debug("the URL will be called is : " + inputUrlAdapter.toString());
		
		if (!this.localSessionFactory.isPlotExist(root)) {
			throw new PlotNotFoundException("Could not find the plot id in context ",new Exception());
		}
		
		StatefulKnowledgeSession sfkSession = null;
		try{
			sfkSession = this.localSessionFactory.getLocalStatefulSession(root);
			Map<String,Class<?>> clsesName = this.localSessionFactory.getRutimeClasses(root);
			//最外层是规则引擎加载的Class
			for (Map.Entry<String,Class<?>> clsEntry : clsesName.entrySet()) {
				String clsKey = clsEntry.getKey();
				Class<?> clsVale = clsEntry.getValue();
				Object invBean = clsVale.newInstance();
				for (Map.Entry<String, Map<String, Object>> beanMapEntry: inpp.getBeansMap().entrySet()) {
					if (clsKey.endsWith(beanMapEntry.getKey())) {
						ReflectGetValue.setFieldValue(beanMapEntry.getValue(), invBean);
					}
				}
				sfkSession.insert(invBean);
			}
		} catch (Exception e){
			e.printStackTrace();
			throw new DroolsInsertException("an error  is found during insert or invoke bean",e);
		}

		//执行规则
		try{
			if (notNullValues(processID))
				sfkSession.startProcess(processID);
			
			sfkSession.fireAllRules();
		} catch (Exception e) {
			e.printStackTrace();
			throw new DroolsFireException("an error is found during run all rules or start a process",e);
		}
		
		try {
			Collection<Object> rtObjs = sfkSession.getObjects();
			for (Object rtObj : rtObjs) {
				for (int i = 0; i < outClsArr.length; i++) {
					if (outClsArr[i].equals(rtObj.getClass().getSimpleName())) {
						Map<String, Map<String, Object>> rtBeanMap = new HashMap<String, Map<String, Object>>();
						rtBeanMap.put(outClsArr[i], ReflectGetValue.getFileValue(rtObj));
						oppm.setBeansMap(rtBeanMap);
					}
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
			throw new GetFiredObjectException("an error is found duringg get objects from fired session",e);
		}
		finally {
			if (sfkSession != null) {
				sfkSession = null;
			}
		}
		return oppm;
	}

	private boolean notNullValues(String processID) {
		return processID!= null && !processID.equals(EMPTY) && processID.length() != 0;
	}

	public LocalSessionFactory getLocalSessionFactory() {
		return localSessionFactory;
	}

	public void setLocalSessionFactory(LocalSessionFactory localSessionFactory) {
		this.localSessionFactory = localSessionFactory;
	};

	
	
}
